library HttpRequest; {$mode objfpc}{$H+} { The purpose of this code is to create a C-style Library (DLL) for sending HTTP requests which can be used by other applications, like ADOxx. This is pretty much done in one "private" function that does all the work and after that several other functions are exposed/epxorted in the dll that simply call the previous "private" function. This way there is only one place where the magic happens (in case of necessary maintenance or improvement) while the exposed/exported functions can provide a nice interface (apropriate default values, overloading etc.). Additionally it provides functions to support some things when making HTTP requests, like encoding text fit for 'application/x-www-form-urlencoded'. } // Created by Patrik. //TODO add also functions that allow to provide a string containing bytes (same as the one returned) which is then sent. This is necessary to transfer content with NUL bytes! // This shouldn't be too difficult using a TStringStream where the individual bytes are written and which is then passed as is to httpclient.RequestBody // the number can be transformed to the corresponding ANSI character using the chr function (e.g. chr(0) is the character for the NUL byte). //TODO fphttpclient doesn't seem to support any special 'Parameters' handling for the requests. // In other words it's the job of the "user" of the library to put in the parameters correctly. // In GET requests they are simply part of the URL as the query, but in POST their format/syntax depends on the used Content-Type. // For POST the two content types I've mostly seen when searching the internet were 'application/x-www-form-urlencoded' and // 'multipart/form-data', but we also have a service that use POST with JSON instead. So there doesn't seem to be a standard // on how this is handled. Other Web-Frameworks can have support for automatically puting parameters in the request correctly // depending on the content type. So one possibility for extending this would be to handle some of the content-types. // Also I don't know about parameters for other methods (like DELETE) uses sysutils, classes, fphttpclient, base64, fpjson, jsonparser; // sysutils is necessary for Exception handling. // classes is used for the TStringStream and TStrings and possibly other classes. // fphttpclient provides the TFPHttpClient class which is used to send the requests. // base64 is needed for the EncodeStringBase64 and DecodeStringBase64 function. // fpjson and jsonparser are used to process the the request headers. // NOTE: All of those units are part of Free Pascal and fall under it's License (COPYING.FPC). type // Enumeration of the possible styles of providing the input and returning the response body. // Currently Input as ByteArray isn't supported. HRDataFormat = (PlainString, Base64String, ByteArray); // A type to easier handle the "allowed" character sets for the HRUrlEncode function. TCharSet = set of char; const // The JS allowed character list is based on the Java Script encodeURIComponent() function which seems to be the most strict one (from Firefox and Chrome) AllowedCharactersJS = ['!', ''''..'*', '-'..'.', '0'..'9', 'A'..'Z', '_', 'a'..'z', '~']; // The intermediate allowed character list is based on several sources from the internet, including the URI specification AllowedCharactersInter = ['!', '"', '$', '''', '(', ')', '*', ',', '-', '.', '/', '0'..'9', ';', 'A'..'Z', '_', 'a'..'z', '~']; // The lax allowed character list encodes all non-ASCII characters, ASCII control characters (0x00 to 0x20 and 0x7F), # (0x23), % (0x25), & (0x26), + (0x2B), = (0x3D) and ? (0x3F) AllowedCharactersLax = ['!', '"', '$', ''''..'*', ','..'<', '>', '@'..'~']; // The very lax allowed character list encodes all non-ASCII characters, ASCII control characters (0x00 to 0x20 and 0x7F), % (0x25), & (0x26) and = (0x3D) AllowedCharactersVeryLax = ['!'..'$', ''''..'<', '>'..'~']; // General function to make an HTTP Call covering all possibilities we consider. { This function is not meant to be accessed directly, isntead it is a central implementation used by the other functions exposed in the DLL. Basic authentication has been tested using the Tomcat Manager App. The return value is the HTTP-Status code. Can be 0 if an exception occurred before the server was contacted. Input Patameters: method - which HTTP method should be used. Common methods: GET, POST, PUT, DELETE, OPTIONS, HEAD, TRACE, CONNECT address - the URL where the request should be sent. uname - the username to be used with authentication. If one of uname or upw has a length of > 0 then both will be set. This allows to have authentication with an empty username or password. upw - the user password to be used with authentication. If one of uname of or upw has a length of > 0 then both will be set. This allows to have authentication with an empty username or password. reqheaders - headers to be sent as part of the request. Should use a format that is valid JSON and the keys and values are strings. Yes, this unfortunately means that arrays or complex objects are not supported! reqbody - the body content that should be sent with the request. respbodytype - uses the HRReturnType enumeration to specify if and how the response body should be provided. In every case it has to be a string (since that is the type of the output parameter), but this controlls the content of the string. By default it will be made a byte-array. Note that this array omits the opening and closing parentheses, since they differ from language to language. reqbodytype - uses the HRReturnType enumeration to specify if and how the request body is provided. In every case it has to be a string (since that is the type of the input parameter), but this controlls the content of the sent string. By default it will use the string "as-is". The Base64String will assume that data has been encoded in base64 and decode it before sending it to the server. This can be necessary when the data to be sent contains NUL bytes, since PChar expects a null-terminated string. Currently "ByteArray" is not supported for the input. Output Parameters: respheaders - an output parameter containing the headers of the response in a format that is valid JSON and the keys and values are strings. Yes, this unfortunately means that arrays or complex objects are not supported! respbody - an output parameter which has the content of the body from the response. In case of errors it can contain a message with details about the error. Some errors might come from this program while others can originate from the server. } function HRPrivateHttpCall(method, address, uname, upw, reqheaders, reqbody : PChar; respbodytype : HRDataFormat; reqbodytype : HRDataFormat; out respheaders, respbody : PChar) : longint; cdecl; var httpclient : TFPHttpClient; jsob_reqheader : TJSONObject; srm_respbody : TStringStream; sts_respheader : TStrings; str_respheader : String; str_respbody : String; i : integer; colpos : integer; str_bodybyte : String; begin // Creating the important objects httpclient := TFPHttpClient.Create(Nil); srm_respbody := TStringStream.Create(''); try // First we configure the request httpclient.AllowRedirect := True; // Try adding any passed request headers. if (Length(reqheaders) > 0) then begin jsob_reqheader := TJSONObject(GetJSON(reqheaders)); for i := 0 to (jsob_reqheader.Count-1) do begin httpclient.AddHeader(jsob_reqheader.Names[i], jsob_reqheader.Get(jsob_reqheader.Names[i])); end; jsob_reqheader.Free; end; // If a username or password has been provided, then set those in the client if (Length(uname) > 0) OR (Length(upw) > 0) then begin httpclient.UserName := uname; httpclient.Password := upw; end; // Set the request body if reqbodytype = Base64String then httpclient.RequestBody := TStringStream.Create(DecodeStringBase64(reqbody)) else httpclient.RequestBody := TStringStream.Create(reqbody); // Then we send the request httpclient.HTTPMethod(method, address, srm_respbody, [200]); // And now we start processing the response // First the response headers. TStrings uses '=' as the separator instead of the ':' specified by HTTP, so we have to parse the headers ourselves. sts_respheader := httpclient.ResponseHeaders; str_respheader := '{ '; for i := 0 to (sts_respheader.Count-1) do begin colpos := pos(':', sts_respheader[i]); // if it's one of the "pseudo" headers that starts with a colon then we look for the next colon if colpos = 1 then begin colpos := pos(':', copy(sts_respheader[i], 2, Length(sts_respheader[i])-1)); end; if colpos > 0 then begin str_respheader := str_respheader + '"' + StringReplace(trim(copy(sts_respheader[i], 1, colpos-1)), '"', '\"', [rfReplaceAll]) + '": "' + StringReplace(trim(copy(sts_respheader[i], colpos+1, Length(sts_respheader[i])-colpos)), '"', '\"', [rfReplaceAll]) + '"'; if i < (sts_respheader.Count-1) then str_respheader := str_respheader + ', '; end; end; str_respheader := str_respheader + ' }'; // Next we store the body, with the different types we allow (PlainString, Base64 encoded, an "array" of bytes). str_respbody := srm_respbody.DataString; HRPrivateHttpCall := httpclient.ResponseStatusCode; except // should something have gone wrong we set the three outputs to apropriate values. on E: Exception do begin str_respheader := '{}'; // #13 is carriage return, #10 is line feed. By their powers combined we get a new line. str_respbody := ('ERROR: Request to ' + address + ' failed, because: ' + #13#10 + E.ClassName + ': ' + E.message + #13#10 + 'Message from server: ' + httpclient.ResponseStatusText); HRPrivateHttpCall := httpclient.ResponseStatusCode; end; end; // Here we encode the body properly to the desired type. // Why here? So that any error messages from this library are also encoded! // It is more important for us to stick to the desired type then enforce readability of error messages. if respbodytype = PlainString then respbody := PChar(str_respbody) else if respbodytype = Base64String then respbody := PChar(EncodeStringBase64(str_respbody)) else begin str_bodybyte := ''; for i := 1 to (Length(str_respbody)) do begin str_bodybyte := str_bodybyte + IntToStr(Byte(str_respbody[i])); if i < (Length(str_respbody)) then str_bodybyte := str_bodybyte + ','; end; respbody := PChar(str_bodybyte); end; respheaders := PChar(str_respheader); httpclient.Free; end; // Function that performs an HTTP call and provides the response body as a string. { method should specify which HTTP method should be used. address should be the url of the server to contact. reqheaders and reqbody are the Request Headers and Request Body. respheaders and respbody are the Headers and Body of the response. The return value contains the resulting HTTP-Status code, or is 0 if something went wrong before the HTTP request was sent. The headers should/will use JSON notation where the keys and values are strings. The request body should be a string with the content and is sent as is. Unfortunately since we are dealing with null-terminated strings this does not allow to send NUL bytes as part of the request. The respons body will be a string of the returned content. This can be a problem in some cases, when using null-terminated strings and where the content contains NUL characters (\0), like a PNG imgage. In such a case use HRHttpCallBase64 or HRHttpCallBytes. } function HRHttpCall(method, address, reqheaders, reqbody : PChar; out respheaders, respbody : PChar) : longint; cdecl; begin HRHttpCall := HRPrivateHttpCall(method, address, PChar(''), PChar(''), reqheaders, reqbody, PlainString, PlainString, respheaders, respbody); end; // Function that performs an HTTP call with authentication and provides the response body as a string. { method should specify which HTTP method should be used. address should be the url of the server to contact. uname and upw should specify the username and password to use. reqheaders and reqbody are the Request Headers and Request Body. respheaders and respbody are the Headers and Body of the response. The return value contains the resulting HTTP-Status code, or is 0 if something went wrong before the HTTP request was sent. The headers should/will use JSON notation where the keys and values are strings. The request body should be a string with the content and is sent as is. Unfortunately since we are dealing with null-terminated strings this does not allow to send NUL bytes as part of the request. The respons body will be a string of the returned content. This can be a problem in some cases, when using null-terminated strings and where the content contains NUL characters (\0), like a PNG imgage. In such a case use HRHttpCallBase64 or HRHttpCallBytes. } function HRAuthHttpCall(method, address, uname, upw, reqheaders, reqbody : PChar; out respheaders, respbody : PChar) : longint; cdecl; begin HRAuthHttpCall := HRPrivateHttpCall(method, address, uname, upw, reqheaders, reqbody, PlainString, PlainString, respheaders, respbody); end; // Function that performs an HTTP call and provides the response body as a base64 encoded string. { method should specify which HTTP method should be used. address should be the url of the server to contact. reqheaders and reqbody are the Request Headers and Request Body. respheaders and respbody are the Headers and Body of the response. The return value contains the resulting HTTP-Status code, or is 0 if something went wrong before the HTTP request was sent. The headers should/will use JSON notation where the keys and values are strings. The request body should be a string with the content and is sent as is. Unfortunately since we are dealing with null-terminated strings this does not allow to send NUL bytes as part of the request. The respons body will be a string of the returned content encoded as base64. } function HRHttpCallBase64(method, address, reqheaders, reqbody : PChar; out respheaders, respbody : PChar) : longint; cdecl; begin HRHttpCallBase64 := HRPrivateHttpCall(method, address, PChar(''), PChar(''), reqheaders, reqbody, Base64String, PlainString, respheaders, respbody); end; // Function that performs an HTTP call with authentication and provides the response body as a base64 encoded string. { method should specify which HTTP method should be used. address should be the url of the server to contact. uname and upw should specify the username and password to use. reqheaders and reqbody are the Request Headers and Request Body. respheaders and respbody are the Headers and Body of the response. The return value contains the resulting HTTP-Status code, or is 0 if something went wrong before the HTTP request was sent. The headers should/will use JSON notation where the keys and values are strings. The request body should be a string with the content and is sent as is. Unfortunately since we are dealing with null-terminated strings this does not allow to send NUL bytes as part of the request. The respons body will be a string of the returned content encoded as base64. } function HRAuthHttpCallBase64(method, address, uname, upw, reqheaders, reqbody : PChar; out respheaders, respbody : PChar) : longint; cdecl; begin HRAuthHttpCallBase64 := HRPrivateHttpCall(method, address, uname, upw, reqheaders, reqbody, Base64String, PlainString, respheaders, respbody); end; // Function that performs an HTTP call and provides the response body as a string containing the individual bytes (in decimal system) separated by commas. { method should specify which HTTP method should be used. address should be the url of the server to contact. reqheaders and reqbody are the Request Headers and Request Body. respheaders and respbody are the Headers and Body of the response. The return value contains the resulting HTTP-Status code, or is 0 if something went wrong before the HTTP request was sent. The headers should/will use JSON notation where the keys and values are strings. The request body should be a string with the content and is sent as is. Unfortunately since we are dealing with null-terminated strings this does not allow to send NUL bytes as part of the request. The respons body will be a string, where the returned content is provided as a comma separated list of the bytes (in the decimal system). } function HRHttpCallBytes(method, address, reqheaders, reqbody : PChar; out respheaders, respbody : PChar) : longint; cdecl; begin HRHttpCallBytes := HRPrivateHttpCall(method, address, PChar(''), PChar(''), reqheaders, reqbody, ByteArray, PlainString, respheaders, respbody); end; // Function that performs an HTTP call with authentication and provides the response body as a string containing the individual bytes (in decimal system) separated by commas { method should specify which HTTP method should be used. address should be the url of the server to contact. uname and upw should specify the username and password to use. reqheaders and reqbody are the Request Headers and Request Body. respheaders and respbody are the Headers and Body of the response. The return value contains the resulting HTTP-Status code, or is 0 if something went wrong before the HTTP request was sent. The headers should/will use JSON notation where the keys and values are strings. The request body should be a string with the content and is sent as is. Unfortunately since we are dealing with null-terminated strings this does not allow to send NUL bytes as part of the request. The respons body will be a string, where the returned content is provided as a comma separated list of the bytes (in the decimal system). } function HRAuthHttpCallBytes(method, address, uname, upw, reqheaders, reqbody : PChar; out respheaders, respbody : PChar) : longint; cdecl; begin HRAuthHttpCallBytes := HRPrivateHttpCall(method, address, uname, upw, reqheaders, reqbody, ByteArray, PlainString, respheaders, respbody); end; // Function that performs an HTTP call and provides the response body as a string. { method should specify which HTTP method should be used. address should be the url of the server to contact. reqheaders and reqbody are the Request Headers and Request Body. respheaders and respbody are the Headers and Body of the response. The return value contains the resulting HTTP-Status code, or is 0 if something went wrong before the HTTP request was sent. The headers should/will use JSON notation where the keys and values are strings. The request body should be a string with the passed content encoded in base64 and is decoded before it is sent. If you want to send the data still encoded as base64 then use the HRHttpCall function. The respons body will be a string of the returned content. This can be a problem in some cases, when using null-terminated strings and where the content contains NUL characters (\0), like a PNG imgage. In such a case use HRHttpCallBase64 or HRHttpCallBytes. } function HRHttpCallIn64(method, address, reqheaders, reqbody : PChar; out respheaders, respbody : PChar) : longint; cdecl; begin HRHttpCallIn64 := HRPrivateHttpCall(method, address, PChar(''), PChar(''), reqheaders, reqbody, PlainString, Base64String, respheaders, respbody); end; // Function that performs an HTTP call with authentication and provides the response body as a string. { method should specify which HTTP method should be used. address should be the url of the server to contact. uname and upw should specify the username and password to use. reqheaders and reqbody are the Request Headers and Request Body. respheaders and respbody are the Headers and Body of the response. The return value contains the resulting HTTP-Status code, or is 0 if something went wrong before the HTTP request was sent. The headers should/will use JSON notation where the keys and values are strings. The request body should be a string with the passed content encoded in base64 and is decoded before it is sent. If you want to send the data still encoded as base64 then use the HRHttpCall function. The respons body will be a string of the returned content. This can be a problem in some cases, when using null-terminated strings and where the content contains NUL characters (\0), like a PNG imgage. In such a case use HRHttpCallBase64 or HRHttpCallBytes. } function HRAuthHttpCallIn64(method, address, uname, upw, reqheaders, reqbody : PChar; out respheaders, respbody : PChar) : longint; cdecl; begin HRAuthHttpCallIn64 := HRPrivateHttpCall(method, address, uname, upw, reqheaders, reqbody, PlainString, Base64String, respheaders, respbody); end; // Function that performs an HTTP call and provides the response body as a base64 encoded string. { method should specify which HTTP method should be used. address should be the url of the server to contact. reqheaders and reqbody are the Request Headers and Request Body. respheaders and respbody are the Headers and Body of the response. The return value contains the resulting HTTP-Status code, or is 0 if something went wrong before the HTTP request was sent. The headers should/will use JSON notation where the keys and values are strings. The request body should be a string with the passed content encoded in base64 and is decoded before it is sent. If you want to send the data still encoded as base64 then use the HRHttpCall function. The respons body will be a string of the returned content encoded as base64. } function HRHttpCallBase64In64(method, address, reqheaders, reqbody : PChar; out respheaders, respbody : PChar) : longint; cdecl; begin HRHttpCallBase64In64 := HRPrivateHttpCall(method, address, PChar(''), PChar(''), reqheaders, reqbody, Base64String, Base64String, respheaders, respbody); end; // Function that performs an HTTP call with authentication and provides the response body as a base64 encoded string. { method should specify which HTTP method should be used. address should be the url of the server to contact. uname and upw should specify the username and password to use. reqheaders and reqbody are the Request Headers and Request Body. respheaders and respbody are the Headers and Body of the response. The return value contains the resulting HTTP-Status code, or is 0 if something went wrong before the HTTP request was sent. The headers should/will use JSON notation where the keys and values are strings. The request body should be a string with the passed content encoded in base64 and is decoded before it is sent. If you want to send the data still encoded as base64 then use the HRHttpCall function. The respons body will be a string of the returned content encoded as base64. } function HRAuthHttpCallBase64In64(method, address, uname, upw, reqheaders, reqbody : PChar; out respheaders, respbody : PChar) : longint; cdecl; begin HRAuthHttpCallBase64In64 := HRPrivateHttpCall(method, address, uname, upw, reqheaders, reqbody, Base64String, Base64String, respheaders, respbody); end; // Function that performs an HTTP call and provides the response body as a string containing the individual bytes (in decimal system) separated by commas. { method should specify which HTTP method should be used. address should be the url of the server to contact. reqheaders and reqbody are the Request Headers and Request Body. respheaders and respbody are the Headers and Body of the response. The return value contains the resulting HTTP-Status code, or is 0 if something went wrong before the HTTP request was sent. The headers should/will use JSON notation where the keys and values are strings. The request body should be a string with the passed content encoded in base64 and is decoded before it is sent. If you want to send the data still encoded as base64 then use the HRHttpCall function. The respons body will be a string, where the returned content is provided as a comma separated list of the bytes (in the decimal system). } function HRHttpCallBytesIn64(method, address, reqheaders, reqbody : PChar; out respheaders, respbody : PChar) : longint; cdecl; begin HRHttpCallBytesIn64 := HRPrivateHttpCall(method, address, PChar(''), PChar(''), reqheaders, reqbody, ByteArray, Base64String, respheaders, respbody); end; // Function that performs an HTTP call with authentication and provides the response body as a string containing the individual bytes (in decimal system) separated by commas { method should specify which HTTP method should be used. address should be the url of the server to contact. uname and upw should specify the username and password to use. reqheaders and reqbody are the Request Headers and Request Body. respheaders and respbody are the Headers and Body of the response. The return value contains the resulting HTTP-Status code, or is 0 if something went wrong before the HTTP request was sent. The headers should/will use JSON notation where the keys and values are strings. The request body should be a string with the passed content encoded in base64 and is decoded before it is sent. If you want to send the data still encoded as base64 then use the HRHttpCall function. The respons body will be a string, where the returned content is provided as a comma separated list of the bytes (in the decimal system). } function HRAuthHttpCallBytesIn64(method, address, uname, upw, reqheaders, reqbody : PChar; out respheaders, respbody : PChar) : longint; cdecl; begin HRAuthHttpCallBytesIn64 := HRPrivateHttpCall(method, address, uname, upw, reqheaders, reqbody, ByteArray, Base64String, respheaders, respbody); end; // Function that encodes a provided text in the URL encoding style to allow sending it for example in a application/x-www-form-urlencoded request. { text is the text that should be encoded. This implementation processes it character for character. allowed is a set of characters that don't have to be encoded. Every character that is not part of this set will be encoded with %xx where xx is the hexadecimal code. Check the constants provided at the beginning. } function HRUrlEncode(const text : PChar; const allowed : TCharSet) : PChar; cdecl; var i : integer; len : integer; res : PChar; begin // Check if anything has to be escaped and if yes then calculate how long the result string should be len := StrLen(text); for i := 0 to (StrLen(text)-1) do begin if (not (text[i] in allowed)) then begin Inc(len, 2); end; end; // Allocate anough room for the result HRUrlEncode := StrAlloc(len+1); if (len = StrLen(text)) then // If there isn't anything to encode then we just set the return to the text as it is begin HRUrlEncode := StrCopy(HRUrlEncode, text); end else // Otherwise process the input and encode any characters that aren't allowed using their code value begin res := HRUrlEncode; for i := 0 to (StrLen(text)-1) do begin if (not (text[i] in allowed)) then // If a character has to be encoded, then do it properly begin res^ := '%'; Inc(res); StrFmt(res, '%.2x', [ord(text[i])]); Inc(res, 2); end else // Otherwise just use that character begin res^ := text[i]; Inc(res); end; end; res^ := #0; end; end; // Function that encodes a provided text in the URL encoding style similar to Java Script's encodeURIComponent() function. { text is the text that should be encoded. The list of characters which aren't encoded is based on the Java Script encodeURIComponent() function, which is mostly alpha numeric and a few simple punctuations !'()*-._~ } function HRUrlEncodeJS(const text : PChar) : PChar; cdecl; begin HRUrlEncodeJS := HRUrlEncode(text, AllowedCharactersJS); end; // Function that encodes a provided text in the URL encoding style, encoding a bit less than Java Script. { text is the text that should be encoded. The list of characters which aren't encoded is based on several sources from the internet, including the URI specification. } function HRUrlEncodeInter(const text : PChar) : PChar; cdecl; begin HRUrlEncodeInter := HRUrlEncode(text, AllowedCharactersInter); end; // Function that encodes a provided text in the URL encoding style encoding only few printable ASCII characters. { text is the text that should be encoded. Encodes all non-ASCII characters, ASCII control characters (0x00 to 0x20 and 0x7F), # (0x23), % (0x25), & (0x26), + (0x2B), = (0x3D) and ? (0x3F) } function HRUrlEncodeLax(const text : PChar) : PChar; cdecl; begin HRUrlEncodeLax := HRUrlEncode(text, AllowedCharactersLax); end; // Function that encodes a provided text in the URL encoding style encoding only the printable ASCII characters which have a meaning in the query part of a URL (escape %, next parameter & and has value =). { text is the text that should be encoded. Encodes all non-ASCII characters, ASCII control characters (0x00 to 0x20 and 0x7F), % (0x25), & (0x26) and = (0x3D) } function HRUrlEncodeVeryLax(const text : PChar) : PChar; cdecl; begin HRUrlEncodeVeryLax := HRUrlEncode(text, AllowedCharactersVeryLax); end; // Function that takes a simple JSON object and creates a query parameter string using URL encoding style to allow sending it for example in a application/x-www-form-urlencoded request. { paramsjson a string representing a shallow JSON object. This is then used to create the result which follows the query parameter specification instead (name1=value1&name2=value2&name3=value3 ...) allowed is a set of characters that don't have to be encoded. Every character that is not part of this set will be encoded with %xx where xx is the hexadecimal code. Check the constants provided at the beginning. } function HRUrlEncodeQuery(const paramsjson : PChar; const allowed : TCharSet) : PChar; cdecl; var jsob_params : TJSONObject; sl_result : TStringList; i : integer; begin sl_result := TStringList.Create; sl_result.Delimiter := '&'; try jsob_params := TJSONObject(GetJSON(paramsjson)); for i := 0 to (jsob_params.Count-1) do begin sl_result.Add(HRUrlEncode(PAnsiChar(jsob_params.Names[i]), allowed) + '=' + HRUrlEncode(PAnsiChar(jsob_params.Elements[jsob_params.Names[i]].AsString), allowed)); end; jsob_params.Free; HRUrlEncodeQuery := PChar(sl_result.DelimitedText); except // should something have gone wrong we set the output to empty. on E: Exception do begin HRUrlEncodeQuery := PChar(''); end; end; end; // Function that takes a simple JSON object and creates a query parameter string using URL encoding style similar to Java Script's encodeURIComponent() function. { paramsjson is the JSON object as a string. The list of characters which aren't encoded is based on the Java Script encodeURIComponent() function, which is mostly alpha numeric and a few simple punctuations !'()*-._~ } function HRUrlEncodeQueryJS(const paramsjson : PChar) : PChar; cdecl; begin HRUrlEncodeQueryJS := HRUrlEncodeQuery(paramsjson, AllowedCharactersJS); end; // Function that takes a simple JSON object and creates a query parameter string using URL encoding style, encoding a bit less than Java Script. { paramsjson is the JSON object as a string. The list of characters which aren't encoded is based on several sources from the internet, including the URI specification. } function HRUrlEncodeQueryInter(const paramsjson : PChar) : PChar; cdecl; begin HRUrlEncodeQueryInter := HRUrlEncodeQuery(paramsjson, AllowedCharactersInter); end; // Function that takes a simple JSON object and creates a query parameter string using URL encoding style encoding only few printable ASCII characters. { paramsjson is the JSON object as a string. Encodes all non-ASCII characters, ASCII control characters (0x00 to 0x20 and 0x7F), # (0x23), % (0x25), & (0x26), + (0x2B), = (0x3D) and ? (0x3F) } function HRUrlEncodeQueryLax(const paramsjson : PChar) : PChar; cdecl; begin HRUrlEncodeQueryLax := HRUrlEncodeQuery(paramsjson, AllowedCharactersLax); end; // Function that takes a simple JSON object and creates a query parameter string using URL encoding style encoding only the printable ASCII characters which have a meaning in the query part of a URL (escape %, next parameter & and has value =). { paramsjson is the JSON object as a string. Encodes all non-ASCII characters, ASCII control characters (0x00 to 0x20 and 0x7F), % (0x25), & (0x26) and = (0x3D) } function HRUrlEncodeQueryVeryLax(const paramsjson : PChar) : PChar; cdecl; begin HRUrlEncodeQueryVeryLax := HRUrlEncodeQuery(paramsjson, AllowedCharactersVeryLax); end; //Exposing functions and procedures to be used by other applications exports HRHttpCall, HRAuthHttpCall, HRHttpCallBase64, HRAuthHttpCallBase64, HRHttpCallBytes, HRAuthHttpCallBytes, HRHttpCallIn64, HRAuthHttpCallIn64, HRHttpCallBase64In64, HRAuthHttpCallBase64In64, HRHttpCallBytesIn64, HRAuthHttpCallBytesIn64, HRUrlEncodeJS, HRUrlEncodeInter, HRUrlEncodeLax, HRUrlEncodeVeryLax, HRUrlEncodeQueryJS, HRUrlEncodeQueryInter, HRUrlEncodeQueryLax, HRUrlEncodeQueryVeryLax; begin end.